package com.domino.client.mvc.impl.ai;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Random;
import java.util.Set;

import com.domino.shared.Player;
import com.domino.shared.PlayerId;
import com.domino.shared.model.Board;
import com.domino.shared.model.Brick;
import com.domino.shared.model.Color;
import com.domino.shared.model.Point;
import com.domino.shared.model.SimpleBoard;

public class Amber implements Bot {
	
	private Player player;
	private Color botColor;
	//private Color humanColor;
	private Random rand = new Random(new Date().getTime());
	private long timeStampStop;
	private int searchDepth;
	private int percent;
	
	public Amber(int searchDepth, Color botColor, String name, int percent) {
		this.botColor = botColor;
		//this.humanColor = Color.getOpposite(botColor);
		this.searchDepth = searchDepth;
		player = new Player(new PlayerId("client-bot"), name, null);
		this.percent = percent;
	}

	@Override
	public Player getPlayer() {
		return player;
	}
	
	private static final int MAX_ALGO_TIME_MS = 10*1000; // ten seconds

	@Override
	public Brick getNexMove(Board board) {	
		timeStampStop = new Date().getTime() + MAX_ALGO_TIME_MS;
		// 1. give each availble move a point
		Set<Point> moves = board.getAvailblePoints(botColor);
		if (moves.isEmpty()) 
		{
			throw new IllegalStateException();
		}
	
		List<Decision> decs = new ArrayList<Decision>(moves.size());

//		StringBuilder sb = new StringBuilder();
//		sb.append(botColor.toString());
//		sb.append(" Start [");
		for (Point point : moves) {
			SimpleBoard nextBoard = new SimpleBoard((SimpleBoard) board);
			nextBoard.putBrick(new Brick(point,botColor));
			int score = evalueateMove(nextBoard, searchDepth);
//			sb.append(score);
//			sb.append(" ");
			decs.add(new Decision(point, score));
		}
//		sb.append("] -> ");
		if (decs.isEmpty()) {
			throw new IllegalStateException();
		}
	
		Collections.sort(decs);
		
		// some randomness
		Iterator<Decision> iterator = decs.iterator();
		Decision dec = iterator.next();
		int factor = decs.size();
		while (rand.nextInt(100)<percent && 
				rand.nextInt(factor)==0 && 
				iterator.hasNext() ) {
			dec = iterator.next();
		}
		Brick brick = new Brick(dec.getPoint(), botColor);
//		sb.append(dec.getScore());
//		System.out.println(sb.toString());
		return brick;
	}
	/*
	function minimax(node,depth)
	   if depth <= 0 then
	      -- positive values are good for the maximizing player
	      -- negative values are good for the minimizing player
	      return objective_value(node)
	   end
	   -- maximizing player is (+1)
	   -- minimizing player is (-1)
	   local alpha = node.player * INFINITY
	   
	   local child = next_child(node,nil)
	   while child ~= nil do
	      local score = minimax(child,depth-1)
	      alpha = node.player==1 and math.max(alpha,score) or math.min(alpha,score)
	      child = next_child(node,child)
	   end

	   return alpha
	end*/


	private int evalueateMove(Board board, int depth) {
		if (new Date().getTime()>timeStampStop) {
			return -1;
		}
		
		if (depth<=0) {
			return evaluateBoard(board);
		}
		
		Color colorToMove = board.getNextColor();
		if (colorToMove==null) {
			return evaluateBoard(board);
		}
		
		Set<Point> moves = board.getAvailblePoints(colorToMove);
		
//		StringBuilder sb = new StringBuilder();
//		sb.append(colorToMove.toString());
//		sb.append(" ");
//		sb.append(depth);
//		sb.append(" [");
		
		int levelScore = botColor.equals(colorToMove) ? Integer.MIN_VALUE : Integer.MAX_VALUE;
		for (Point point : board.getAvailblePoints(colorToMove)) {
			SimpleBoard nextBoard = new SimpleBoard((SimpleBoard) board);
			nextBoard.putBrick(new Brick(point, colorToMove));
			int localScore = evalueateMove(nextBoard, depth-1);
//			sb.append(localScore);
//			sb.append(" ");
			
			levelScore = botColor.equals(colorToMove) ? Math.max(localScore, levelScore) :
				Math.min(localScore, levelScore);
		}
//		sb.append("] -> ");
//		sb.append(levelScore);
//		System.out.println(sb.toString());
		
		return levelScore;
	}

	private int evaluateBoard(Board board) {
		/*
		 * Corners 8
		 * Borders 4
		 * Next to borders 1
		 * Others 2
		 */
		
		return board.getScore(botColor);
	}
}

class Decision implements Comparable {
	
	Point point;
	Integer score;
	
	public Decision(Point point, int score) {
		this.point = point;
		this.score = score;
	}

	public Integer getScore() {
		return score;
	}

	@Override
	public int compareTo(Object other) {
		if (other instanceof Decision) {
			return -score.compareTo(((Decision) other).score);
		} else {
			throw new IllegalStateException("Upps");
		}
	}

	public Point getPoint() {
		return point;
	}

	@Override
	public String toString() {
		return "Decision [point=" + point + ", score=" + score + "]";
	}
	
	
}

class TimeOutException extends Exception {
	
}
